AWS CDK(TypeScript)で Python ランタイムの Lambda 関数を実装してみた

AWS CDK(TypeScript)で Python ランタイムの Lambda 関数を実装してみた

Clock Icon2023.07.31

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、CX 事業本部 Delivery 部の若槻です。

今回は、AWS CDK(TypeScript)で Python ランタイムの Lambda 関数を実装する方法を確認してみます。

使用したのは Amazon Lambda Python Library です。現在は Alpha 版となります。

このライブラリの PythonFunction を使うことにより、NodejsFunctionと同様に、モジュールのパッケージングをよしなにやってくれます。

試してみた

AWS CDK(TypeScript)で Python ランタイムの Lambda 関数を実装してみます。

プロジェクト作成

CDK プロジェクトを初回作成します。言語は TypeScript です。(Python ではありません)

mkdir cdk_demo_project
cd cdk_demo_project
cdk init --language typescript

Amazon Lambda Python Library 導入

Amazon Lambda Python Library をインストールします。

npm i  -D @aws-cdk/aws-lambda-python-alpha

Lambda 関数および CDK コード

Lambda 関数を配置するディレクトリを作成します。

mkdir src & mkdir src/lambda & mkdir src/lambda/hello
touch src/lambda/hello/index.py

次の内容の Lambda 関数を作成します。

def handler(event, context):
    return {
        'statusCode': 200,
        'body': 'Hello, CDK!'
    }

PythonFunction を使って Lambda 関数を CDK コードで実装します。

import { aws_lambda, Stack, StackProps } from 'aws-cdk-lib';
import { PythonFunction } from '@aws-cdk/aws-lambda-python-alpha';
import { Construct } from 'constructs';

export class CdkDemoProjectStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps) {
    super(scope, id, props);

    new PythonFunction(this, 'helloFunction', {
      functionName: 'helloFunction',
      runtime: aws_lambda.Runtime.PYTHON_3_11,
      entry: 'src/lambda/hello',
      handler: 'handler',
    });
  }
}

デプロイ

さてここから CDK Deploy により Lambda 関数をデプロイしていくのですが、PythonFunction で Lambda 関数をビルドする上で必要な Docker 周りでかなりの試行錯誤があったので、その記録を残しておきます。

CDK Deploy をしようとすると Synth でエラーとなりました。ここで Docker の起動が必要であることに気が付きます。

$ cdk deploy                                
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/private/asset-staging.js:2
`).map((line,idx)=>`${idx===0?firstLine:padding}${line}`)};const reason=proc.signal!=null?`signal ${proc.signal}`:`status ${proc.status}`,command=[prog,...args.map(arg=>/[^a-z0-9_-]/i.test(arg)?JSON.stringify(arg):arg)].join(" ");throw new Error([`${prog} exited with ${reason}`,...prependLines("--> STDOUT:  ",proc.stdout)??[],...prependLines("--> STDERR:  ",proc.stderr)??[],`--> Command: ${command}`].join(`
                                                                                                                                                                                                                                            ^
Error: docker exited with status 1
--> Command: docker build -t cdk-ab3de628ee1e2f62b9079f422a090e9043f44ecd2ffb1c3a0c647e07830330bd --platform "linux/amd64" --build-arg "IMAGE=public.ecr.aws/sam/build-python3.11" "/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib"
    at dockerExec (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/private/asset-staging.js:2:237)
    at Function.fromBuild (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/bundling.js:1:4124)
    at new Bundling (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/bundling.ts:100:39)
    at Function.bundle (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/bundling.ts:61:44)
    at new PythonFunction (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/function.ts:75:22)
    at new CdkDemoProjectStack (/Users/wakatsuki.ryuta/projects/cdk_demo_project/lib/cdk_demo_project-stack.ts:9:5)
    at Object.<anonymous> (/Users/wakatsuki.ryuta/projects/cdk_demo_project/bin/cdk_demo_project-stack.ts:6:1)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)
    at Module.m._compile (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)

Subprocess exited with error 1

Docker を起動させます。

$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

するとエラーが変わりました。

$ cdk deploy
[+] Building 0.3s (3/3) FINISHED                                                                                                                                                                                 
 => [internal] load build definition from Dockerfile                                                                                                                                                        0.0s
 => => transferring dockerfile: 1.28kB                                                                                                                                                                      0.0s
 => [internal] load .dockerignore                                                                                                                                                                           0.0s
 => => transferring context: 2B                                                                                                                                                                             0.0s
 => ERROR [internal] load metadata for public.ecr.aws/sam/build-python3.11:latest                                                                                                                           0.3s
------
 > [internal] load metadata for public.ecr.aws/sam/build-python3.11:latest:
------
failed to solve with frontend dockerfile.v0: failed to create LLB definition: failed to do request: Head "https://public.ecr.aws/v2/sam/build-python3.11/manifests/latest": Failed to lookup host: public.ecr.aws
/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/private/asset-staging.js:2
`).map((line,idx)=>`${idx===0?firstLine:padding}${line}`)};const reason=proc.signal!=null?`signal ${proc.signal}`:`status ${proc.status}`,command=[prog,...args.map(arg=>/[^a-z0-9_-]/i.test(arg)?JSON.stringify(arg):arg)].join(" ");throw new Error([`${prog} exited with ${reason}`,...prependLines("--> STDOUT:  ",proc.stdout)??[],...prependLines("--> STDERR:  ",proc.stderr)??[],`--> Command: ${command}`].join(`
                                                                                                                                                                                                                                            ^
Error: docker exited with status 1
--> Command: docker build -t cdk-ab3de628ee1e2f62b9079f422a090e9043f44ecd2ffb1c3a0c647e07830330bd --platform "linux/amd64" --build-arg "IMAGE=public.ecr.aws/sam/build-python3.11" "/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib"
    at dockerExec (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/private/asset-staging.js:2:237)
    at Function.fromBuild (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/bundling.js:1:4124)
    at new Bundling (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/bundling.ts:100:39)
    at Function.bundle (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/bundling.ts:61:44)
    at new PythonFunction (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/function.ts:75:22)
    at new CdkDemoProjectStack (/Users/wakatsuki.ryuta/projects/cdk_demo_project/lib/cdk_demo_project-stack.ts:9:5)
    at Object.<anonymous> (/Users/wakatsuki.ryuta/projects/cdk_demo_project/bin/cdk_demo_project-stack.ts:6:1)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)
    at Module.m._compile (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)

Subprocess exited with error 1

ここで Docker Desktop のバージョンが古いことに気が付いたのでアップグレードします。するとさらにエラーが変わりました。

$ cdk deploy
[+] Building 2.2s (4/4) FINISHED                                                                                                                                                                                 
 => [internal] load build definition from Dockerfile                                                                                                                                                        0.0s
 => => transferring dockerfile: 37B                                                                                                                                                                         0.0s
 => [internal] load .dockerignore                                                                                                                                                                           0.0s
 => => transferring context: 2B                                                                                                                                                                             0.0s
 => ERROR [internal] load metadata for public.ecr.aws/sam/build-python3.11:latest                                                                                                                           2.1s
 => [auth] aws:: sam/build-python3.11:pull token for public.ecr.aws                                                                                                                                         0.0s
------
 > [internal] load metadata for public.ecr.aws/sam/build-python3.11:latest:
------
failed to solve with frontend dockerfile.v0: failed to create LLB definition: unexpected status code [manifests latest]: 403 Forbidden
/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/private/asset-staging.js:2
`).map((line,idx)=>`${idx===0?firstLine:padding}${line}`)};const reason=proc.signal!=null?`signal ${proc.signal}`:`status ${proc.status}`,command=[prog,...args.map(arg=>/[^a-z0-9_-]/i.test(arg)?JSON.stringify(arg):arg)].join(" ");throw new Error([`${prog} exited with ${reason}`,...prependLines("--> STDOUT:  ",proc.stdout)??[],...prependLines("--> STDERR:  ",proc.stderr)??[],`--> Command: ${command}`].join(`
                                                                                                                                                                                                                                            ^
Error: docker exited with status 1
--> Command: docker build -t cdk-ab3de628ee1e2f62b9079f422a090e9043f44ecd2ffb1c3a0c647e07830330bd --platform "linux/amd64" --build-arg "IMAGE=public.ecr.aws/sam/build-python3.11" "/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib"
    at dockerExec (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/private/asset-staging.js:2:237)
    at Function.fromBuild (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/bundling.js:1:4124)
    at new Bundling (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/bundling.ts:100:39)
    at Function.bundle (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/bundling.ts:61:44)
    at new PythonFunction (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/function.ts:75:22)
    at new CdkDemoProjectStack (/Users/wakatsuki.ryuta/projects/cdk_demo_project/lib/cdk_demo_project-stack.ts:9:5)
    at Object.<anonymous> (/Users/wakatsuki.ryuta/projects/cdk_demo_project/bin/cdk_demo_project-stack.ts:6:1)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)
    at Module.m._compile (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)

Subprocess exited with error 1

このエラーは下記が参考になりました。

Docker で ECR にログインをします。

aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws

すると Synth が通り、デプロイまで行えました。

$ cdk deploy                                                                                                        
[+] Building 135.5s (7/7) FINISHED                                                                                                                                                          docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                                                                                                                        0.0s
 => => transferring dockerfile: 1.28kB                                                                                                                                                                      0.0s
 => [internal] load .dockerignore                                                                                                                                                                           0.0s
 => => transferring context: 2B                                                                                                                                                                             0.0s
 => [internal] load metadata for public.ecr.aws/sam/build-python3.11:latest                                                                                                                                 2.4s
 => [auth] aws:: sam/build-python3.11:pull token for public.ecr.aws                                                                                                                                         0.0s
 => [1/2] FROM public.ecr.aws/sam/build-python3.11@sha256:ad08c7e702a7c2b5c13c60de6180ca4bc97be781a72063a618784596cbda65c6                                                                                 32.9s
 => => resolve public.ecr.aws/sam/build-python3.11@sha256:ad08c7e702a7c2b5c13c60de6180ca4bc97be781a72063a618784596cbda65c6                                                                                  0.0s
 => => sha256:10f153b3bd74382059f5423e00bb91447ae450a3b566ac8dd34b9072ac49dcf8 2.64kB / 2.64kB                                                                                                              0.0s
 => => sha256:29a91f87d93088b3ae82542459d827edd30a0bd0d1c2709f2e53982c258cb8b6 87.36kB / 87.36kB                                                                                                            0.2s
 => => sha256:877e2bfc6ba89a3fc51d12d92799e17d3ec8afefbf7cc868de9e35d078b8faa9 417B / 417B                                                                                                                  0.3s
 => => sha256:ad08c7e702a7c2b5c13c60de6180ca4bc97be781a72063a618784596cbda65c6 772B / 772B                                                                                                                  0.0s
 => => sha256:79a77e7c1be9a2c4f77ead609e8d8b7162377bb6905b2a244c7964d74d8c8762 2.51MB / 2.51MB                                                                                                              1.4s
 => => sha256:0e9b5ea721abee27f3e34c651182ec82103b32067ef6858f5d432d6b10c10099 7.04kB / 7.04kB                                                                                                              0.0s
 => => extracting sha256:29a91f87d93088b3ae82542459d827edd30a0bd0d1c2709f2e53982c258cb8b6                                                                                                                   0.0s
 => => sha256:c091401fd8a0aa9d9e34453038b9e08154298a521f3f460a3f0bb2b946e4629d 128.93MB / 128.93MB                                                                                                          6.5s
 => => extracting sha256:877e2bfc6ba89a3fc51d12d92799e17d3ec8afefbf7cc868de9e35d078b8faa9                                                                                                                   0.0s
 => => sha256:2e3380a3fb2644cc0f902d3001f7d8235102e976431959b3ecdcd7d9b947378f 15.31kB / 15.31kB                                                                                                            0.7s
 => => sha256:8ceec474a49056b91a037e8617c75feac54d4f014219d0ab05cfb0854f06f171 224.66MB / 224.66MB                                                                                                         13.6s
 => => extracting sha256:79a77e7c1be9a2c4f77ead609e8d8b7162377bb6905b2a244c7964d74d8c8762                                                                                                                   0.0s
 => => sha256:4b67a36e95c8b60c0373e05feeba88d4875956eac55167b880a8c838cc6f0e14 55.82MB / 55.82MB                                                                                                           11.9s
 => => sha256:0344a919057170918f8b5889a6c39857e0776683651910a7769bcb0cc283dc73 79.85MB / 79.85MB                                                                                                           15.0s
 => => extracting sha256:c091401fd8a0aa9d9e34453038b9e08154298a521f3f460a3f0bb2b946e4629d                                                                                                                   5.5s
 => => sha256:fcf86f09cae9b81c5101817003e642f3262b3787a61f1bff6f0ac052032940e7 205.46kB / 205.46kB                                                                                                         12.2s
 => => sha256:1ac678504b71b45f9cfa05f7a25ae9510e1c2cb432fdcac3a59d58c94c7ad10e 103.92kB / 103.92kB                                                                                                         12.4s
 => => extracting sha256:2e3380a3fb2644cc0f902d3001f7d8235102e976431959b3ecdcd7d9b947378f                                                                                                                   0.0s
 => => extracting sha256:8ceec474a49056b91a037e8617c75feac54d4f014219d0ab05cfb0854f06f171                                                                                                                  10.6s
 => => extracting sha256:4b67a36e95c8b60c0373e05feeba88d4875956eac55167b880a8c838cc6f0e14                                                                                                                   2.3s
 => => extracting sha256:0344a919057170918f8b5889a6c39857e0776683651910a7769bcb0cc283dc73                                                                                                                   4.7s
 => => extracting sha256:fcf86f09cae9b81c5101817003e642f3262b3787a61f1bff6f0ac052032940e7                                                                                                                   0.0s
 => => extracting sha256:1ac678504b71b45f9cfa05f7a25ae9510e1c2cb432fdcac3a59d58c94c7ad10e                                                                                                                   0.0s
 => [2/2] RUN     python -m venv /usr/app/venv &&     mkdir /tmp/pip-cache &&     chmod -R 777 /tmp/pip-cache &&     pip install --upgrade pip &&     mkdir /tmp/poetry-cache &&     chmod -R 777 /tmp/po  99.7s
 => exporting to image                                                                                                                                                                                      0.5s
 => => exporting layers                                                                                                                                                                                     0.5s
 => => writing image sha256:392b68bf00a6d9cee956f0a5322492f8853b9f35d6f8678fbc47591504c80a38                                                                                                                0.0s 
 => => naming to docker.io/library/cdk-ab3de628ee1e2f62b9079f422a090e9043f44ecd2ffb1c3a0c647e07830330bd                                                                                                     0.0s 
                                                                                                                                                                                                                 
What's Next?                                                                                                                                                                                                     
  View summary of image vulnerabilities and recommendations → docker scout quickview
Bundling asset CdkDemoProjectStack/helloFunction/Code/Stage...
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
sending incremental file list
index.py

sent 217 bytes  received 35 bytes  504.00 bytes/sec
total size is 105  speedup is 0.42

✨  Synthesis time: 139.39s

CdkDemoProjectStack:  start: Building 8efbea89c33394485407976945c492b1e7e99ef18a4c8c8544f234f2e5490a7d:current_account-current_region
CdkDemoProjectStack:  success: Built 8efbea89c33394485407976945c492b1e7e99ef18a4c8c8544f234f2e5490a7d:current_account-current_region
CdkDemoProjectStack:  start: Building 603ed967932afd7da0b3109bd50ddbcea827870adcdc7e1bf0ed9bb0e738d776:current_account-current_region
CdkDemoProjectStack:  success: Built 603ed967932afd7da0b3109bd50ddbcea827870adcdc7e1bf0ed9bb0e738d776:current_account-current_region
CdkDemoProjectStack:  start: Publishing 8efbea89c33394485407976945c492b1e7e99ef18a4c8c8544f234f2e5490a7d:current_account-current_region
CdkDemoProjectStack:  start: Publishing 603ed967932afd7da0b3109bd50ddbcea827870adcdc7e1bf0ed9bb0e738d776:current_account-current_region
CdkDemoProjectStack:  success: Published 603ed967932afd7da0b3109bd50ddbcea827870adcdc7e1bf0ed9bb0e738d776:current_account-current_region
CdkDemoProjectStack:  success: Published 8efbea89c33394485407976945c492b1e7e99ef18a4c8c8544f234f2e5490a7d:current_account-current_region
CdkDemoProjectStack: deploying... [1/1]
CdkDemoProjectStack: creating CloudFormation changeset...

 ✅  CdkDemoProjectStack

✨  Deployment time: 32.56s

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/CdkDemoProjectStack/00c2aac0-2abc-11ee-85b9-0a99c3b7c389

✨  Total time: 171.95s

デプロイした Lambda 関数を実行すると正常に動作していますね。

$ aws lambda invoke \
  --function-name helloFunction \
  response.json
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
$ cat response.json
{"statusCode": 200, "body": "Hello, CDK!"}

Lambda 関数にパッケージを追加

PythonFunction で実装する Lambda 関数にパッケージを追加したい場合は、entry パスに

  • requirements.txt
  • Pipfile
  • poetry.lock

のいずれかを含めれば、よしなにパッケージングをしてデプロイしてくれます。

今回は Poetry を使ってみます。pytz をインストールします。

cd src/lambda/hello
poetry init
poetry add pytz

すると src/lambda/hello/poetry.lock が作成されます。また同ディレクトリに pyproject.toml も作成されますが、これは Git で管理不要なので.gitignoreに追記しておきましょう。

Lambda 関数を pytz を使うように修正して、CDK Deploy します。

from datetime import datetime
from pytz import timezone

def handler(event, context):
    tokyo_dt = datetime.now(timezone('Asia/Tokyo'))

    return {
        'statusCode': 200,
        'body': tokyo_dt.strftime('%Y-%m-%d %H:%M:%S')
    }

デプロイした Lambda 関数を実行すると、pytz が使用された結果が返ってきています。良さそうですね。

$ aws lambda invoke \
  --function-name helloFunction \
  response.json
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
$ cat response.json
{"statusCode": 200, "body": "2023-07-31 23:13:09"}

ちなみにパッケージングのその他の方法として同ライブラリの PythonLayerVersion も使用可能です。こちらは Lambda 関数のコードとは別にレイヤーとしてパッケージングされます。

おわりに

AWS CDK(TypeScript)で Python ランタイムの Lambda 関数を実装してみました。

Python や Docker を使うのは久しぶりで四苦八苦しましたが、なんとかデプロイまで行えました。参考になれば幸いです。

参考

以上

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.